﻿using JetBrains.Annotations;
using System;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
using Xms.Abp.Domain;
using Xms.Abp.Domain.Repositories;
using Xms.Abp.MultiTenancy;

namespace TenantManagement;

[Dependency(ReplaceServices = true)]
[ExposeServices(typeof(ITenantStore), typeof(IMultiTenantStore))]
public class MultiTenantStore : XmsDomainService, IMultiTenantStore, ITransientDependency
{
    protected IXmsRepository<Tenant, Guid> TenantRepository => LazyInject<IXmsRepository<Tenant, Guid>>();
    //TODO: Debug cache
    //protected ITenantCacheContext Cache => LazyInject<ITenantCacheContext>();

    private TenantData Parse(Tenant tenant)
    {
        if (tenant == null) return null;
        return new TenantData
        {
            Id = tenant.Id,
            Name = tenant.Name,
            DisplayName = tenant.DisplayName,
            Favicon = tenant.Favicon,
            Domains = new Xms.Abp.Data.Domains(),
            ConnectionStrings = new Volo.Abp.Data.ConnectionStrings(),
            IsActive = tenant.IsEnabled,
            LargeLogo = tenant.LargeLogo,
            Logo = tenant.Logo,
            Remark = tenant.Remark,
        };
    }

    public virtual async Task<TenantData> FindAsync(string name)
    {
        var tenant = await TenantRepository.GetAsync(r => r.Name == name, true);
        return Parse(tenant);
        //return (await GetCacheItemAsync(null, name));
    }

    public virtual async Task<TenantData> FindAsync(Guid id)
    {
        var tenant = await TenantRepository.GetAsync(id, true);
        return Parse(tenant);
        //return (await GetCacheItemAsync(id, null));
    }

    [Obsolete("Use FindAsync method.")]
    public virtual TenantData Find(string name)
    {
        return AsyncHelper.RunSync(async () => await FindAsync(name));
        //return AsyncHelper.RunSync(async () => await GetCacheItemAsync(null, name));
    }

    [Obsolete("Use FindAsync method.")]
    public virtual TenantData Find(Guid id)
    {
        return AsyncHelper.RunSync(async () => await FindAsync(id));
        //return AsyncHelper.RunSync(async () => await GetCacheItemAsync(id, null));
    }

    //protected virtual async Task<TenantConfigurations> GetCacheItemAsync(Guid? id, string name)
    //{
    //    var cacheKey = CalculateCacheKey(id, name);

    //    var cacheItem = await Cache.Tenants.GetAsync(cacheKey, considerUow: true);
    //    if (cacheItem != null)
    //    {
    //        return cacheItem;
    //    }

    //    if (id.HasValue)
    //    {
    //        using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities!
    //        {
    //            var tenant = await TenantRepository.FindAsync(id.Value);
    //            return await SetCacheAsync(cacheKey, tenant);
    //        }
    //    }

    //    if (!name.IsNullOrWhiteSpace())
    //    {
    //        using (CurrentTenant.Change(null)) //TODO: No need this if we can implement to define host side (or tenant-independent) entities!
    //        {
    //            var tenant = await (await TenantRepository.GetQueryableAsync()).Include(r => r.Domains).Include(r => r.ConnectionStrings).FirstOrDefaultAsync(r => r.Name == name);
    //            return await SetCacheAsync(cacheKey, tenant);
    //        }
    //    }

    //    throw new AbpException("Both id and name can't be invalid.");
    //}

    //protected virtual async Task<TenantConfigurations> SetCacheAsync(string cacheKey, [CanBeNull] Tenant tenant)
    //{
    //    var tenantConfigurations = tenant != null ? ObjectMapper.Map<Tenant, TenantConfigurations>(tenant) : null;
    //    await Cache.Tenants.SetAsync(cacheKey, tenantConfigurations, considerUow: true);
    //    return tenantConfigurations;
    //}

    public static string CalculateCacheKey(Guid? id, string name)
    {
        return id.HasValue ? id.Value.ToString() : name;
    }

    [Obsolete("Use GetAllAsync method.")]
    public TenantData[] GetAll()
    {
        return AsyncHelper.RunSync(async () => await TenantRepository.GetListAsync(true)).Select(r => Parse(r)).ToArray();
    }

    public async Task<TenantData[]> GetAllAsync()
    {
        return (await TenantRepository.GetListAsync(true)).Select(r => Parse(r)).ToArray();
    }

    async Task<TenantConfiguration> ITenantStore.FindAsync(string name)
    {
        return await FindAsync(name);
    }

    async Task<TenantConfiguration> ITenantStore.FindAsync(Guid id)
    {
        return await FindAsync(id);
    }

    [Obsolete("Use FindAsync method.")]
    TenantConfiguration ITenantStore.Find(string name)
    {
        return Find(name);
    }

    [Obsolete("Use FindAsync method.")]
    TenantConfiguration ITenantStore.Find(Guid id)
    {
        return Find(id);
    }
}
